const { reject, add } = require('lodash')
// 简答题
// (1)JS异步编程: JS执行环境是单线程运行, 不会去等待这个任务的结束才开始下一个任务,开启过后立即往后执行下一个任务,后续逻辑一般会通过回调函数的方式定义
// (2)消息队列(Queue): 存放需要执行的程序,按照先进先出原则, 当调用栈为空时将消息队列中排在首位的程序塞入调用栈
// (3)事件循环(Event loop): 是一个程序结构, 用来等待和发送消息和事件. 执行完全局代码后, 首先执行微任务队列(一轮一轮)直到微任务队列为空, 然后执行宏任务队列(按照消息队列的顺序)直到为空
// (4)宏任务: 页面加载、各种事件、全局代码、定时器等, 如: setTimeout、setInterval、setImmediate (Node)等
// (5)微任务: 通常来说就是需要在当前task执行结束后立即执行的任务, 如: process.nextTick (Node)、Promise.then()


// 代码题

const fp = require('lodash/fp')
const MyPromise = require('./promise-realization/myPromise')

// 一 将下面异步代码使用Promise的方式改进
Promise.resolve('Hello').then(value => value + 'lagou').then(value => console.log(value + 'I ∆ U'))


// 二 基于一下代码完成下面的四个联系
const cars = [
    {
        name: 'Ferrari FF',
        horsepower: 660,
        dollar_value: 700000,
        in_stock: true
    },
    {
        name: 'Spyker C12 Zagato',
        horsepower: 650,
        dollar_value: 648000,
        in_stock: false
    },
    {
        name: 'Jaguar XHR-X',
        horsepower: 550,
        dollar_value: 132000,
        in_stock: false
    },
    {
        name: 'Audi R8',
        horsepower: 525,
        dollar_value: 114200,
        in_stock: false
    },
    {
        name: 'Aston Martin One-77',
        horsepower: 750,
        dollar_value: 1850000,
        in_stock: true
    },
    {
        name: 'Pagani Huayra',
        horsepower: 700,
        dollar_value: 1300000,
        in_stock: false
    },
]
// 练习一
// let isLastInStock = function(cars){
//     let last_car = fp.last(cars)
//     return fp.prop('in_stock', last_car)
// }
let isLastInStock = fp.flowRight(fp.prop('in_stock'), fp.last)
console.log(isLastInStock(cars))


// 练习二
let isFirstName = fp.flowRight(fp.prop('name'), fp.first)
console.log(isFirstName(cars))

// 练习三
let _average = xs => {
    return fp.reduce(fp.add, 0, xs) / xs.length
}

// let averageDollarValue = cars => {
//     let dollar_value = fp.map(cars => {
//         return cars.dollar_value
//     }, cars)
//     return _average(dollar_value)
// }

let averageDollarValue = fp.compose(_average, fp.map(cars => cars.dollar_value))
console.log(averageDollarValue(cars))


// 练习四
let _underScore = fp.replace(/\W+/g, '_')
// let sanitizeNames = fp.flowRight(fp.map(fp.flowRight(_underScore, fp.toLower, fp.split(' '))))
let sanitizeNames = fp.map(fp.flowRight(_underScore, fp.toLower, fp.split(' ')))
console.log(sanitizeNames(['Hello World']))


// 三 基于下面提供的代码,完成后续的四个练习
const { MayBe, Container } = require('./support')

// 练习一
let maybe = MayBe.of([5, 6, 1])
let ex1 = () => {
    return maybe.map(fp.map(value => fp.add(value, 2)))
}
console.log(ex1())

// 练习二
let xs = Container.of(['do', 'ray', 'me', 'fa', 'so', 'la', 'ti', 'do'])
let ex2 = () => {
    return xs.map(value => fp.first(value))
}
console.log(ex2())


// 练习三
let safeProp = fp.curry((x, o) => {
    return MayBe.of(o[x])
})
let user = { id: 2, name: 'Albert' }

let ex3 = () => {
    return fp.first(safeProp('name', user)._value)
}
console.log(ex3())

// 练习四
let ex4 = n => {
    return MayBe.of(n).map(value => parseInt(value))._value
}
console.log(ex4(1.3))


// 四、手写Promise源码

// promise有三种状态 pending 等待 fulfilled 成功 rejected 失败
// 且状态改变只能从pending -> fulfilled(从等待到成功) 或者 pending -> rejected(从等待到失败) 且状态改变后不可更改
// 声明三种状态常量
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

// 创建Promise类
class MyProise {
    // Promise 对象需要传入一个执行器函数 该执行器函数会立即执行 并且该执行器函数有两个参数成功回调 失败回调
    // 并且两个参数resolve和reject是来更改函数状态的
    // resolve -> fulfilled(从等待到成功)
    // reject -> rejected(从等待到失败)
    constructor(executor) {
        // 使用trycatch是为了捕获执行器函数中的运行异常 如出现异常则执行catch逻辑 将Promise类的函数状态更改为rejected并将失败原因返回
        try {
            // 执行执行器函数 参数为更新函数状态的两个函数
            executor(this.resolve, this.reject)
        } catch (error) {
            // 捕获执行器函数异常 将Promise类的函数状态更改为rejected
            this.reject(error)
        }
    }
    //记录函数状态并初始化函数状态为pending
    status = PENDING
    // 记录函数成功后的返回值 作为成功返回值给then方法成功回调
    value = undefined
    // 记录函数失败后的返回值 作为失败原因给then方法失败回调
    reason = undefined
    // 收集函数状态为成功的回调函数
    successCallback = []
    // 收集函数状态为失败的回调函数
    failCallback = []
    // 执行器成功函数
    resolve = value => {
        // 函数状态更改后不可改变 若函数状态不为等待 拒绝向下执行
        if (this.status !== PENDING) return
        // 将函数状态更改为成功
        this.status = FULFILLED
        // 成功后的返回值
        this.value = value
        // Promise函数具有thenable接口且可以多次调用 导致可能会有多个回调函数执行 
        // 使用while循环是因为数组变化导致循环体不成立时自动停止
        while (this.successCallback.length) this.successCallback.shift()()
    }
    // 执行器失败函数
    reject = reason => {
        // 函数状态更改后不可改变 若函数状态不为等待 拒绝向下执行
        if (this.status !== PENDING) return
        // 将函数状态更改为失败
        this.status = REJECTED
        // 失败后的返回值
        this.reason = reason
        // Promise函数具有thenable接口且可以多次调用 导致可能会有多个回调函数执行 
        // 使用while循环是因为数组变化导致循环体不成立时自动停止
        while (this.failCallback.length) this.failCallback.shift()()
    }
    // 类的thenable接口
    then(successCallback, failCallback) {
        // then方法具有thenable接口 可以链式调用 需要返回一个promise对象
        // 创建Promise对象
        let promise = new MyPromise((resolve, reject) => {
            // then函数的状态取决于回调函数运行状态 现在判断状态
            if (this.status === FULFILLED) {
                // 状态为成功

                // 在此逻辑中我们会用到变量promise 为确定变量promise有值 必须确定new MyPromise执行完毕
                // 故我们使用定时器setTimeout 不是为了延迟而是为了异步执行以确定变量promise有值
                setTimeout(() => {
                    // 使用trycatch是为了捕获函数中的运行异常 如出现异常则执行catch逻辑 将Promise类的函数状态更改为rejected并将失败原因返回
                    try {
                        let result = successCallback(this.value)
                        // result的值可能存在两种情况普通值和promise对象
                        // 如果为普通值直接调用resolve返回
                        // 如果为promise对象则根据promise的执行结果决定调用resolve还是reject
                        // 并且then方法返回的promise对象不能是当前then方法返回的对象, 否则会产生Promise对象的循环调用, JS会报错
                        // 此逻辑我们用一个函数来实现 因此我们需要传入四个参数 
                        // 1. then方法返回值promise 2. then方法成功回调的返回值 3. 更改状态为成功resolve 4. 更改状态为失败reject
                        resolvePromise(promise, result, resolve, reject)
                    } catch (error) {
                        // 捕获函数异常 将Promise类的函数状态更改为rejected并将失败原因返回
                        reject(error)
                    }
                }, 0)
            } else if (this.status === REJECTED) {
                // 状态为失败

                // 在此逻辑中我们会用到变量promise 为确定变量promise有值 必须确定new MyPromise执行完毕
                // 故我们使用定时器setTimeout 不是为了延迟而是为了异步执行以确定变量promise有值
                setTimeout(() => {
                    // 使用trycatch是为了捕获函数中的运行异常 如出现异常则执行catch逻辑 将Promise类的函数状态更改为rejected并将失败原因返回
                    try {
                        let result = failCallback(this.value)
                        // result的值可能存在两种情况普通值和promise对象
                        // 如果为普通值直接调用resolve返回
                        // 如果为promise对象则根据promise的执行结果决定调用resolve还是reject
                        // 并且then方法返回的promise对象不能是当前then方法返回的对象, 否则会产生Promise对象的循环调用, JS会报错
                        // 此逻辑我们用一个函数来实现 因此我们需要传入四个参数 
                        // 1. then方法返回值promise 2. then方法成功回调的返回值 3. 更改状态为成功resolve 4. 更改状态为失败reject
                        resolvePromise(promise, result, resolve, reject)
                    } catch (error) {
                        // 捕获函数异常 将Promise类的函数状态更改为rejected并将失败原因返回
                        reject(error)
                    }
                }, 0)
            } else {
                // 状态为等待
                // 此时需要等待then方法执行完毕才能直到函数状态 
                // 故将成功回调和失败回调存储起来 待状态更改之后在调用
                this.successCallback.push(() => {
                    // 在此逻辑中我们会用到变量promise 为确定变量promise有值 必须确定new MyPromise执行完毕
                    // 故我们使用定时器setTimeout 不是为了延迟而是为了异步执行以确定变量promise有值
                    setTimeout(() => {
                        // 使用trycatch是为了捕获函数中的运行异常 如出现异常则执行catch逻辑 将Promise类的函数状态更改为rejected并将失败原因返回
                        try {
                            let result = successCallback(this.value)
                            // result的值可能存在两种情况普通值和promise对象
                            // 如果为普通值直接调用resolve返回
                            // 如果为promise对象则根据promise的执行结果决定调用resolve还是reject
                            // 并且then方法返回的promise对象不能是当前then方法返回的对象, 否则会产生Promise对象的循环调用, JS会报错
                            // 此逻辑我们用一个函数来实现 因此我们需要传入四个参数 
                            // 1. then方法返回值promise 2. then方法成功回调的返回值 3. 更改状态为成功resolve 4. 更改状态为失败reject
                            resolvePromise(promise, result, resolve, reject)
                        } catch (error) {
                            // 捕获函数异常 将Promise类的函数状态更改为rejected并将失败原因返回
                            reject(error)
                        }
                    }, 0)
                })
                this.failCallback.push(() => {
                    // 在此逻辑中我们会用到变量promise 为确定变量promise有值 必须确定new MyPromise执行完毕
                    // 故我们使用定时器setTimeout 不是为了延迟而是为了异步执行以确定变量promise有值
                    setTimeout(() => {
                        // 使用trycatch是为了捕获函数中的运行异常 如出现异常则执行catch逻辑 将Promise类的函数状态更改为rejected并将失败原因返回
                        try {
                            let result = failCallback(this.value)
                            // result的值可能存在两种情况普通值和promise对象
                            // 如果为普通值直接调用resolve返回
                            // 如果为promise对象则根据promise的执行结果决定调用resolve还是reject
                            // 并且then方法返回的promise对象不能是当前then方法返回的对象, 否则会产生Promise对象的循环调用, JS会报错
                            // 此逻辑我们用一个函数来实现 因此我们需要传入四个参数 
                            // 1. then方法返回值promise 2. then方法成功回调的返回值 3. 更改状态为成功resolve 4. 更改状态为失败reject
                            resolvePromise(promise, result, resolve, reject)
                        } catch (error) {
                            // 捕获函数异常 将Promise类的函数状态更改为rejected并将失败原因返回
                            reject(error)
                        }
                    }, 0)
                })
            }
        })
        // 返回then函数的promise对象
        return promise
    }
    // 类的finally方法 参数为回调函数
    // finally方法的内容都会被执行一次
    // finally方法之后可以链式调用then方法
    finally(callback) {
        // 在finally 内部可以通过then方法拿到 当前promise的状态 
        // 并且then方法返回promise对象 可以链式调用then方法
        return this.then(value => {
            // 如果finally的回调函数是promise 需等待回调函数执行完成在将结果返回给之后的链式调用
            return MyProise.resolve(callback()).then(() => value)
        }, reason => {
            // 如果finally的回调函数是promise 需等待回调函数执行完成在将结果返回给之后的链式调用
            return MyProise.resolve(callback()).then(() => {throw reason})
        })
    }
    // 捕获异常
    catch(failCallback){
        // 使用then方法去注册失败回调 并置空成功回调
        return this.then(undefined, failCallback)
    }
    // promise.all()方法 参数为数组 返回结果为数组内的元素顺序(且为promise对象), 不论有无延迟
    // 解决异步并发问题
    static all(array){
        // 声明结果数组 作为all方法的返回值
        let result = []
        // all方法中有可能有promise的异步操作, 为保证参数数组中每一项都有返回值 需对返回结果修改时进行标记(累加处理)
        let index = 0
        return new MyProise((resolve, reject) => {
            // 将结果放入结果数组
            function addData(key, value){
                result[key] = value
                // 累加处理
                index++
                // 当所有结果都有返回值时 执行all方法的成功回调
                if(index === array.length){
                    resolve(result)
                }
            }
            // 遍历当前数组 并且判断循环体是普通值还是promise对象
            for(let i = 0; i < array.length; i++){
                // 劫持循环体
                let current = array[i]
                if(current instanceof MyProise){
                    // 循环体为promise对象 根据其运行结果调用resolve和reject更改状态
                    current.then(value => addData(i, value), reason => reject(reason))
                }else {
                    // 循环体为普通值
                    addData(i, current)
                }
            }
        })
    }
    // Promise.resolve()方法返回一个promise对象
    static resolve (value){
        // 如果value是promise对象直接返回
        if(value instanceof MyProise) return value 
        // 如果value是普通值 返回一个promise对象 并且将value当作成功返回值返回
        return new MyProise(resolve => resolve(value))
    }
}

function resolvePromise(promise, result, resolve, reject) {
    // 如果then方法返回值promise 和 then方法成功回调的返回值 相同
    if (promise === result) {
        // 抛出错误 promise对象循环调用
        return reject(new TypeError('TypeError: Chaining cycle detected for promise #<Promise>'))
    }
    // 如果then方法成功回调的返回值 是promise对象
    if (result instanceof MyProise) {
        // 调用promise对象的then方法 根据执行结果决定调用resolve还是reject
        result.then(resolve, reject)
    } else {
        // 如果then方法成功回调的返回值 是普通值
        resolve(result)
    }
}




















